// Copyright(c) 2011 Matthew McMullan, Jorel Lalicki and Mike Heise, Jorel Lalicki and Mike Heise. See copying.txt for details.

#ifndef __agml_mstatic_f32_4_4_h__
#define __agml_mstatic_f32_4_4_h__

// make sure that this file is never included directly
#ifndef __agml_agml_h__
#error "Include AGML from agml.h only. This file is not a valid entry point for the library."
#endif

namespace agml {
	using namespace construct;
	// also used for non-member templated functions
	#define dep_elements (16/sizeof(t))
	#define dep_height ((((height*sizeof(t)+15)/16))*dep_elements)
	#define dep_data (dep_height*width)
	#define depsimd (dep_height*width)
	template<> struct mstatic<f32,4,4> {
		typedef vstatic<f32,4> row_t;	// length of a row
		typedef vstatic<f32,4> col_t;	// length of a column
		typedef vstatic<f32,4> line_t;	// length of a diagonal. same as width and height in a square matrix.
		union {
			f32 data[16];
			_v128 simd[4];
		};
	// construction
		mstatic() {}
		mstatic(_m_construct_identity) {

			simd[0] = _mm_set_ps(0.0f,0.0f,0.0f,1.0f);
			simd[1] = _mm_set_ps(0.0f,0.0f,1.0f,0.0f);
			simd[2] = _mm_set_ps(0.0f,1.0f,0.0f,0.0f);
			simd[3] = _mm_set_ps(1.0f,0.0f,0.0f,0.0f);
		}
		mstatic(_m_construct_trans, const f32 x, const f32 y, const f32 z) {
			simd[0] = _mm_set_ps(x,   0.0f,0.0f,1.0f);
			simd[1] = _mm_set_ps(y,   0.0f,1.0f,0.0f);
			simd[2] = _mm_set_ps(z,   1.0f,0.0f,0.0f);
			simd[3] = _mm_set_ps(1.0f,0.0f,0.0f,0.0f);
		}
		mstatic(_m_construct_transx, const f32 x) {
			simd[0] = _mm_set_ps(x,   0.0f,0.0f,1.0f);
			simd[1] = _mm_set_ps(0.0f,0.0f,1.0f,0.0f);
			simd[2] = _mm_set_ps(0.0f,1.0f,0.0f,0.0f);
			simd[3] = _mm_set_ps(1.0f,0.0f,0.0f,0.0f);
		}
		mstatic(_m_construct_transy, const f32 y) {
			simd[0] = _mm_set_ps(0.0f,0.0f,0.0f,1.0f);
			simd[1] = _mm_set_ps(y,   0.0f,1.0f,0.0f);
			simd[2] = _mm_set_ps(0.0f,1.0f,0.0f,0.0f);
			simd[3] = _mm_set_ps(1.0f,0.0f,0.0f,0.0f);
		}
		mstatic(_m_construct_transz, const f32 z) {
			simd[0] = _mm_set_ps(0.0f,0.0f,0.0f,1.0f);
			simd[1] = _mm_set_ps(0.0f,0.0f,1.0f,0.0f);
			simd[2] = _mm_set_ps(z,   1.0f,0.0f,0.0f);
			simd[3] = _mm_set_ps(1.0f,0.0f,0.0f,0.0f);
		}
		mstatic(_m_construct_scale, const f32 x, const f32 y, const f32 z) {
			simd[0] = _mm_set_ps(0.0f,0.0f,0.0f,x   );
			simd[1] = _mm_set_ps(0.0f,0.0f,y,   0.0f);
			simd[2] = _mm_set_ps(0.0f,z,   0.0f,0.0f);
			simd[3] = _mm_set_ps(1.0f,0.0f,0.0f,0.0f);
		}
		mstatic(_m_construct_scalex, const f32 x) {
			simd[0] = _mm_set_ps(0.0f,0.0f,0.0f,x   );
			simd[1] = _mm_set_ps(0.0f,0.0f,0.0f,0.0f);
			simd[2] = _mm_set_ps(0.0f,0.0f,0.0f,0.0f);
			simd[3] = _mm_set_ps(1.0f,0.0f,0.0f,0.0f);
		}
		mstatic(_m_construct_scaley, const f32 y) {
			simd[0] = _mm_set_ps(0.0f,0.0f,0.0f,0.0f);
			simd[1] = _mm_set_ps(0.0f,0.0f,y,   0.0f);
			simd[2] = _mm_set_ps(0.0f,0.0f,0.0f,0.0f);
			simd[3] = _mm_set_ps(1.0f,0.0f,0.0f,0.0f);
		}
		mstatic(_m_construct_scalez, const f32 z) {
			simd[0] = _mm_set_ps(0.0f,0.0f,0.0f,0.0f);
			simd[1] = _mm_set_ps(0.0f,0.0f,0.0f,0.0f);
			simd[2] = _mm_set_ps(0.0f,z,   0.0f,0.0f);
			simd[3] = _mm_set_ps(1.0f,0.0f,0.0f,0.0f);
		}
		mstatic(_m_construct_rot, const f32 x, const f32 y, const f32 z) {
			f32 b = cos(x), a = sin(x), d = cos(y), c = sin(y), f = cos(z), e = sin(z);
			f32 cf = c*f, ae = a*e, be = b*e;
	//	xy	{{d,0,c,0},{a*c,b,-a*d,0},{-b*c,a,b*d,0},{0,0,0,1}}
			simd[0] = _mm_set_ps(0.0f,c,-d*e,d*f);
			simd[1] = _mm_set_ps(0.0f,-a*d,b*f-ae*c,be+a*cf);
			simd[2] = _mm_set_ps(0.0f,b*d,be*c+a*f,ae-b*cf);
			simd[3] = _mm_set_ps(1.0f,0.0f,0.0f,0.0f);
		}
		mstatic(_m_construct_rotx, const f32 angle) {
			f32 s = sin(angle);//a
			f32 c = cos(angle);//b
	//		{{1,0,0,0},{0,b,-a,0},{0,a,b,0},{0,0,0,1}}
			simd[0] = _mm_set_ps(0.0f,0.0f,0.0f,1.0f);
			simd[1] = _mm_set_ps(0.0f,s,c,0.0f);
			simd[2] = _mm_set_ps(0.0f,c,-s,0.0f);
			simd[3] = _mm_set_ps(1.0f,0.0f,0.0f,0.0f);
		}
		mstatic(_m_construct_roty, const f32 angle) {
			f32 s = sin(angle);//c
			f32 c = cos(angle);//d
	//		{{d,0,c,0},{0,1,0,0},{-c,0,d,0},{0,0,0,1}}
			simd[0] = _mm_set_ps(0.0f,-s,0.0f,c);
			simd[1] = _mm_set_ps(0.0f,0.0f,1.0f,0.0f);
			simd[2] = _mm_set_ps(0.0f,c,0.0f,s);
			simd[3] = _mm_set_ps(1.0f,0.0f,0.0f,0.0f);
		}
		mstatic(_m_construct_rotz, const f32 angle) {
			f32 s = sin(angle);//e
			f32 c = cos(angle);//f
	//		{{f,-e,0,0},{e,f,0,0},{0,0,1,0},{0,0,0,1}}
			simd[0] = _mm_set_ps(0.0f,0.0f,s,c);
			simd[1] = _mm_set_ps(0.0f,0.0f,c,-s);
			simd[2] = _mm_set_ps(0.0f,1.0f,0.0f,0.0f);
			simd[3] = _mm_set_ps(1.0f,0.0f,0.0f,0.0f);
		}
		mstatic(const mstatic &m) {
			_mm_store_ps(&data[0],	m.simd[0]);
			_mm_store_ps(&data[4],	m.simd[1]);
			_mm_store_ps(&data[8],	m.simd[2]);
			_mm_store_ps(&data[12],	m.simd[3]);
		}
	// comparison
		bool operator==(const mstatic &other) const {
			for (u32 i=0; i < 16; ++i) {
				if(data[i] != other.data[i]) {
					return 0;
				}
			}
			return 1;
		}

		bool operator!=(const mstatic &other) const {
			for (u32 i=0; i < 16; ++i) {
				if(data[i] != other.data[i]) {
					return 1;
				}
			}
			return 0;
		}
	// indexing

		col_t &operator[](u32_fast col) {
			return *( reinterpret_cast<col_t *>(&simd[col]) );
		}
		const col_t &operator[](u32_fast col) const {
			return *( reinterpret_cast<const col_t *>(&simd[col]) );
		}

	// getters
		super_inline const f32& get(const u32_fast x, const u32_fast y) const {
			return data[y+x*4];
		}
		super_inline f32& get(const u32_fast x, const u32_fast y) {
			return data[y+x*4];
		}
		super_inline void get_col(const u32_fast col, col_t &dest) const {
			dest.simd[0] = simd[col];
		}
		super_inline col_t get_col(const u32_fast col) const {
			col_t dest;
			get_col(col,dest);
			return dest;
		}
		super_inline void get_row(const u32_fast row, row_t &dest) const {
			dest[0] = data[row];
			dest[1] = data[row+4];
			dest[2] = data[row+8];
			dest[3] = data[row+12];
		}
		super_inline row_t get_row(const u32_fast row) const {
			row_t dest;
			get_row(row,dest);
			return dest;
		}
		super_inline void get_diag(line_t &dest) {
			dest[0] = data[0];
			dest[1] = data[5];
			dest[2] = data[10];
			dest[3] = data[15];
		}
		line_t super_inline get_diag() {
			line_t dest;
			get_diag(dest);
			return dest;
		}
	// setters
		super_inline void set(const u32_fast x, const u32_fast y, const f32 o) {
			data[y+x*4] = o;
		}
		super_inline void set_row(const u32_fast row, const row_t &o) {
			data[row] = o[0];
			data[row+4] = o[1];
			data[row+8] = o[2];
			data[row+12] = o[3];
		}
		super_inline void set_col(const u32_fast col, const col_t &o) {
			simd[col] = o.simd[0];
		}
		super_inline void set_diag(const line_t &o) {
			data[0] = o[0];
			data[5] = o[1];
			data[10] = o[2];
			data[15] = o[3];
		}
		super_inline void set_identity() {
			simd[0] = _mm_set_ps(0.0f,0.0f,0.0f,1.0f);
			simd[1] = _mm_set_ps(0.0f,0.0f,1.0f,0.0f);
			simd[2] = _mm_set_ps(0.0f,1.0f,0.0f,0.0f);
			simd[3] = _mm_set_ps(1.0f,0.0f,0.0f,0.0f);
		}
		super_inline void set_trans(const f32 x, const f32 y, const f32 z) {
			simd[0] = _mm_set_ps(x,   0.0f,0.0f,1.0f);
			simd[1] = _mm_set_ps(y,   0.0f,1.0f,0.0f);
			simd[2] = _mm_set_ps(z,   1.0f,0.0f,0.0f);
			simd[3] = _mm_set_ps(1.0f,0.0f,0.0f,0.0f);
		}
		super_inline void set_transx(const f32 x) {
			simd[0] = _mm_set_ps(x,   0.0f,0.0f,1.0f);
			simd[1] = _mm_set_ps(0.0f,0.0f,1.0f,0.0f);
			simd[2] = _mm_set_ps(0.0f,1.0f,0.0f,0.0f);
			simd[3] = _mm_set_ps(1.0f,0.0f,0.0f,0.0f);
		}
		super_inline void set_transy(const f32 y) {
			simd[0] = _mm_set_ps(0.0f,0.0f,0.0f,1.0f);
			simd[1] = _mm_set_ps(y,   0.0f,1.0f,0.0f);
			simd[2] = _mm_set_ps(0.0f,1.0f,0.0f,0.0f);
			simd[3] = _mm_set_ps(1.0f,0.0f,0.0f,0.0f);
		}
		super_inline void set_transz(const f32 z) {
			simd[0] = _mm_set_ps(0.0f,0.0f,0.0f,1.0f);
			simd[1] = _mm_set_ps(0.0f,0.0f,1.0f,0.0f);
			simd[2] = _mm_set_ps(z,   1.0f,0.0f,0.0f);
			simd[3] = _mm_set_ps(1.0f,0.0f,0.0f,0.0f);
		}
		super_inline void set_scale(const f32 x, const f32 y, const f32 z) {
			simd[0] = _mm_set_ps(0.0f,0.0f,0.0f,x   );
			simd[1] = _mm_set_ps(0.0f,0.0f,y,   0.0f);
			simd[2] = _mm_set_ps(0.0f,z,   0.0f,0.0f);
			simd[3] = _mm_set_ps(1.0f,0.0f,0.0f,0.0f);
		}
		super_inline void set_scalex(const f32 x) {
			simd[0] = _mm_set_ps(0.0f,0.0f,0.0f,x   );
			simd[1] = _mm_set_ps(0.0f,0.0f,0.0f,0.0f);
			simd[2] = _mm_set_ps(0.0f,0.0f,0.0f,0.0f);
			simd[3] = _mm_set_ps(1.0f,0.0f,0.0f,0.0f);
		}
		super_inline void set_scaley(const f32 y) {
			simd[0] = _mm_set_ps(0.0f,0.0f,0.0f,0.0f);
			simd[1] = _mm_set_ps(0.0f,0.0f,y,   0.0f);
			simd[2] = _mm_set_ps(0.0f,0.0f,0.0f,0.0f);
			simd[3] = _mm_set_ps(1.0f,0.0f,0.0f,0.0f);
		}
		super_inline void set_scalez(const f32 z) {
			simd[0] = _mm_set_ps(0.0f,0.0f,0.0f,0.0f);
			simd[1] = _mm_set_ps(0.0f,0.0f,0.0f,0.0f);
			simd[2] = _mm_set_ps(0.0f,z,   0.0f,0.0f);
			simd[3] = _mm_set_ps(1.0f,0.0f,0.0f,0.0f);
		}
		super_inline void set_rot(const f32 x, const f32 y, const f32 z) {
			f32 b = cos(x), a = sin(x), d = cos(y), c = sin(y), f = cos(z), e = sin(z);
			f32 cf = c*f, ae = a*e, be = b*e;
	//	xy	{{d,0,c,0},{a*c,b,-a*d,0},{-b*c,a,b*d,0},{0,0,0,1}}
			simd[0] = _mm_set_ps(0.0f,c,	-d*e,	d*f);
			simd[1] = _mm_set_ps(0.0f,-a*d,b*f-ae*c,be+a*cf);
			simd[2] = _mm_set_ps(0.0f,b*d,	be*c+a*f,ae-b*cf);
			simd[3] = _mm_set_ps(1.0f,0.0f,0.0f,	0.0f);
		}
		void set_rotx(const f32 angle) {
			f32 s = sin(angle);//a
			f32 c = cos(angle);//b
	//		{{1,0,0,0},{0,b,-a,0},{0,a,b,0},{0,0,0,1}}
			simd[0] = _mm_set_ps(0.0f,0.0f,0.0f,1.0f);
			simd[1] = _mm_set_ps(0.0f,s,c,0.0f);
			simd[2] = _mm_set_ps(0.0f,c,-s,0.0f);
			simd[3] = _mm_set_ps(1.0f,0.0f,0.0f,0.0f);
		}
		super_inline void set_roty(const f32 angle) {
			f32 s = sin(angle);//c
			f32 c = cos(angle);//d
	//		{{d,0,c,0},{0,1,0,0},{-c,0,d,0},{0,0,0,1}}
			simd[0] = _mm_set_ps(0.0f,-s,0.0f,c);
			simd[1] = _mm_set_ps(0.0f,0.0f,1.0f,0.0f);
			simd[2] = _mm_set_ps(0.0f,c,0.0f,s);
			simd[3] = _mm_set_ps(1.0f,0.0f,0.0f,0.0f);
		}
		super_inline void set_rotz(const f32 angle) {
			f32 s = sin(angle);//e
			f32 c = cos(angle);//f
	//		{{f,-e,0,0},{e,f,0,0},{0,0,1,0},{0,0,0,1}}
			simd[0] = _mm_set_ps(0.0f,0.0f,s,c);
			simd[1] = _mm_set_ps(0.0f,0.0f,c,-s);
			simd[2] = _mm_set_ps(0.0f,1.0f,0.0f,0.0f);
			simd[3] = _mm_set_ps(1.0f,0.0f,0.0f,0.0f);
		}
	// addition
		mstatic &operator+=(const mstatic &o) {
			_mm_store_ps(&data[0],_mm_add_ps(simd[0],o.simd[0]));
			_mm_store_ps(&data[4],_mm_add_ps(simd[1],o.simd[1]));
			_mm_store_ps(&data[8],_mm_add_ps(simd[2],o.simd[2]));
			_mm_store_ps(&data[12],_mm_add_ps(simd[3],o.simd[3]));
			return *this;
		}
		mstatic &operator+=(const f32 o) {
			_v128 _o = _mm_set1_ps(o);
			_mm_store_ps(&data[0], _mm_add_ps(simd[0], _o));
			_mm_store_ps(&data[4], _mm_add_ps(simd[1], _o));
			_mm_store_ps(&data[8], _mm_add_ps(simd[2], _o));
			_mm_store_ps(&data[12], _mm_add_ps(simd[3], _o));
			return *this;
		}
		mstatic operator+(const mstatic &o) const {
			mstatic dest;
			add(o,dest);
			return dest;
		}
		mstatic operator+(const f32 &o) const {
			mstatic dest;
			_v128 _o = _mm_set1_ps(o);
			_mm_store_ps(&dest.data[0], _mm_add_ps(simd[0], _o));
			_mm_store_ps(&dest.data[4], _mm_add_ps(simd[1], _o));
			_mm_store_ps(&dest.data[8], _mm_add_ps(simd[2], _o));
			_mm_store_ps(&dest.data[12], _mm_add_ps(simd[3], _o));
			return dest;
		}
		void add(const mstatic &o, mstatic &dest) const {
			_mm_store_ps(&dest.data[0], _mm_add_ps(simd[0], o.simd[0]));
			_mm_store_ps(&dest.data[4], _mm_add_ps(simd[1], o.simd[1]));
			_mm_store_ps(&dest.data[8], _mm_add_ps(simd[2], o.simd[2]));
			_mm_store_ps(&dest.data[12], _mm_add_ps(simd[3], o.simd[3]));
		}


		//optimized matrix subtraction
		mstatic &operator-=(const mstatic &o) {
			_mm_store_ps(&data[0],_mm_sub_ps(simd[0],o.simd[0]));
			_mm_store_ps(&data[4],_mm_sub_ps(simd[1],o.simd[1]));
			_mm_store_ps(&data[8],_mm_sub_ps(simd[2],o.simd[2]));
			_mm_store_ps(&data[12],_mm_sub_ps(simd[3],o.simd[3]));
			return *this;
		}

		mstatic &operator-=(const f32 o) {
			_v128 _o = _mm_set1_ps(o);
			_mm_store_ps(&data[0], _mm_sub_ps(simd[0], _o));
			_mm_store_ps(&data[4], _mm_sub_ps(simd[1], _o));
			_mm_store_ps(&data[8], _mm_sub_ps(simd[2], _o));
			_mm_store_ps(&data[12], _mm_sub_ps(simd[3], _o));
			return *this;
		}

		mstatic operator-(const mstatic &o) const {
			mstatic dest;
			sub(o,dest);
			return dest;
		}

		mstatic operator-(const f32 &o) const {
			mstatic dest;
			_v128 _o = _mm_set1_ps(o);
			_mm_store_ps(&dest.data[0], _mm_sub_ps(simd[0], _o));
			_mm_store_ps(&dest.data[4], _mm_sub_ps(simd[1], _o));
			_mm_store_ps(&dest.data[8], _mm_sub_ps(simd[2], _o));
			_mm_store_ps(&dest.data[12], _mm_sub_ps(simd[3], _o));
			return dest;
		}

		void sub(const mstatic &o, mstatic &dest) const {
			_mm_store_ps(&dest.data[0], _mm_sub_ps(simd[0], o.simd[0]));
			_mm_store_ps(&dest.data[4], _mm_sub_ps(simd[1], o.simd[1]));
			_mm_store_ps(&dest.data[8], _mm_sub_ps(simd[2], o.simd[2]));
			_mm_store_ps(&dest.data[12], _mm_sub_ps(simd[3], o.simd[3]));
		}


		//optimized mstatic<4,4>*=float operator
		mstatic &operator*=(f32 o) {
			const __m128 t = _mm_set1_ps(o);
			_mm_store_ps(&data[0],_mm_mul_ps(simd[0],t));
			_mm_store_ps(&data[4],_mm_mul_ps(simd[1],t));
			_mm_store_ps(&data[8],_mm_mul_ps(simd[2],t));
			_mm_store_ps(&data[12],_mm_mul_ps(simd[3],t));
			return *this;
		}

		mstatic super_inline operator*(f32 o) const {
			mstatic dest;
			_v128 _o = _mm_set1_ps(o);
			_mm_store_ps(&dest.data[0], _mm_mul_ps(simd[0], _o));
			_mm_store_ps(&dest.data[4], _mm_mul_ps(simd[1], _o));
			_mm_store_ps(&dest.data[8], _mm_mul_ps(simd[2], _o));
			_mm_store_ps(&dest.data[12], _mm_mul_ps(simd[3], _o));
			return dest;
		}
		template<u32 c> super_inline mstatic<f32,4,c> operator*(const mstatic<f32,4,c> &o) const {
			mstatic<f32,4,c> dest;
			mult(o,dest);
			return dest;
		}

		void mult(const mstatic<f32,4U,4U> &o, mstatic<f32,4U,4U> &dest) const { //4x4 optimized matrix multiply...slightly faster unrolled.
			const __m128 a = get_col(0).simd[0];
			const __m128 b = get_col(1).simd[0];
			const __m128 c = get_col(2).simd[0];
			const __m128 d = get_col(3).simd[0];
			__m128 t,t2;
					t = _mm_set1_ps(o.data[0]);
					t2 = _mm_mul_ps(a,t);
					t=_mm_set1_ps(o.data[1]);
					t2 = _mm_add_ps(_mm_mul_ps(b,t),t2);
					t=_mm_set1_ps(o.data[2]);
					t2 = _mm_add_ps(_mm_mul_ps(c,t),t2);
					t=_mm_set1_ps(o.data[3]);
					t2 = _mm_add_ps(_mm_mul_ps(d,t),t2);
					_mm_store_ps(&dest.data[0],t2);

					t = _mm_set1_ps(o.data[4]);
					t2 = _mm_mul_ps(a,t);
					t=_mm_set1_ps(o.data[5]);
					t2 = _mm_add_ps(_mm_mul_ps(b,t),t2);
					t=_mm_set1_ps(o.data[6]);
					t2 = _mm_add_ps(_mm_mul_ps(c,t),t2);
					t=_mm_set1_ps(o.data[7]);
					t2 = _mm_add_ps(_mm_mul_ps(d,t),t2);
					_mm_store_ps(&dest.data[4],t2);

					t = _mm_set1_ps(o.data[8]);
					t2 = _mm_mul_ps(a,t);
					t=_mm_set1_ps(o.data[9]);
					t2 = _mm_add_ps(_mm_mul_ps(b,t),t2);
					t=_mm_set1_ps(o.data[10]);
					t2 = _mm_add_ps(_mm_mul_ps(c,t),t2);
					t=_mm_set1_ps(o.data[11]);
					t2 = _mm_add_ps(_mm_mul_ps(d,t),t2);
					_mm_store_ps(&dest.data[8],t2);

					t = _mm_set1_ps(o.data[12]);
					t2 = _mm_mul_ps(a,t);
					t=_mm_set1_ps(o.data[13]);
					t2 = _mm_add_ps(_mm_mul_ps(b,t),t2);
					t=_mm_set1_ps(o.data[14]);
					t2 = _mm_add_ps(_mm_mul_ps(c,t),t2);
					t=_mm_set1_ps(o.data[15]);
					t2 = _mm_add_ps(_mm_mul_ps(d,t),t2);
					_mm_store_ps(&dest.data[12],t2);
		}
	// transformation
		void trans(f32 x, f32 y, f32 z) {
			simd[0] = _mm_add_ps(_mm_mul_ps(_mm_set1_ps(x),simd[4]),simd[0]);
			simd[1] = _mm_add_ps(_mm_mul_ps(_mm_set1_ps(y),simd[4]),simd[1]);
			simd[2] = _mm_add_ps(_mm_mul_ps(_mm_set1_ps(z),simd[4]),simd[2]);
		}
		void transx(f32 x) {
			simd[0] = _mm_add_ps(_mm_mul_ps(_mm_set1_ps(x),simd[4]),simd[0]);
		}
		void transy(f32 y) {
			simd[1] = _mm_add_ps(_mm_mul_ps(_mm_set1_ps(y),simd[4]),simd[1]);
		}
		void transz(f32 z) {
			simd[2] = _mm_add_ps(_mm_mul_ps(_mm_set1_ps(z),simd[4]),simd[2]);
		}
		void scale(f32 x, f32 y, f32 z) {
			simd[0] = _mm_mul_ps(_mm_set1_ps(x),simd[0]);
			simd[1] = _mm_mul_ps(_mm_set1_ps(y),simd[1]);
			simd[2] = _mm_mul_ps(_mm_set1_ps(z),simd[2]);
		}
		void scalex(f32 x) {
			simd[0] = _mm_mul_ps(_mm_set1_ps(x),simd[0]);
		}
		void scaley(f32 y) {
			simd[1] = _mm_mul_ps(_mm_set1_ps(y),simd[1]);
		}
		void scalez(f32 z) {
			simd[2] = _mm_mul_ps(_mm_set1_ps(z),simd[2]);
		}
		void rot(f32 x, f32 y, f32 z) {
			f32 b = cos(x), a = sin(x), d = cos(y), c = sin(y), f = cos(z), e = sin(z);
			f32 cf = c*f, ae = a*e, be = b*e;
			f32 A,B,C,D,E,F,H,I;
			_v128 t0, t1, t2;
			_v128 t;
			A = d*f;
			t = _mm_set1_ps(A);
			D = -d*e;
			t0 = _mm_mul_ps(simd[0],t);
			t = _mm_set1_ps(D);
			B = a*cf;
			t0 = _mm_add_ps(_mm_mul_ps(simd[1],t),t0);
			t = _mm_set1_ps(c);
			E = b*f;
			t0 = _mm_add_ps(_mm_mul_ps(simd[2],t),t0);
			E -= ae*c;
			t = _mm_set1_ps(B);
			B += be;
			t1 = _mm_mul_ps(simd[0],t);
			H = -a*d;
			t = _mm_set1_ps(E);
			t1 = _mm_add_ps(_mm_mul_ps(simd[1],t),t1);
			C = ae-b*cf;
			t = _mm_set1_ps(H);
			t1 = _mm_add_ps(_mm_mul_ps(simd[2],t),t1);
			F = be*c;
			t = _mm_set1_ps(C);
			t2 = _mm_mul_ps(simd[0],t);
			F+= a*f;
			t = _mm_set1_ps(F);
			t2 = _mm_add_ps(_mm_mul_ps(simd[1],t),t2);
			I = b*d;
			t = _mm_set1_ps(I);
			t2 = _mm_add_ps(_mm_mul_ps(simd[2],t),t2);
			simd[0] = t0;
			simd[1] = t1;
			simd[2] = t2;
		}
		void nrot(f32 x, f32 y, f32 z) {
			rotx(x); roty(y); rotz(z);
		}
		void rotxold(f32 x) {
			_v128 s = _mm_set1_ps(sin(x)), c = _mm_set1_ps(cos(x));
			_v128 tmp = _mm_add_ps(_mm_mul_ps(simd[1],c),_mm_mul_ps(simd[2],s));
			simd[2] = _mm_sub_ps(_mm_mul_ps(simd[2],c),_mm_mul_ps(simd[1],s));
			simd[1] = tmp;
		}

		void rotx(f32 x) {
			_v128 s = _mm_set1_ps(sin(x)), c = _mm_set1_ps(cos(x));
			_v128 tmp = _mm_add_ps(_mm_mul_ps(simd[1],c),_mm_mul_ps(simd[2],s));
			simd[2] = _mm_sub_ps(_mm_mul_ps(simd[2],c),_mm_mul_ps(simd[1],s));
			simd[1] = tmp;
		}

		void roty(f32 y) {
			_v128 s = _mm_set1_ps(sin(y)), c = _mm_set1_ps(cos(y));
			_v128 tmp = _mm_sub_ps(_mm_mul_ps(simd[0],c),_mm_mul_ps(simd[2],s));
			simd[2] = _mm_add_ps(_mm_mul_ps(simd[0],s),_mm_mul_ps(simd[2],c));
			simd[0] = tmp;
		}

		void rotz(f32 z) {
			_v128 s = _mm_set1_ps(sin(z)), c = _mm_set1_ps(cos(z));
			_v128 tmp = _mm_add_ps(_mm_mul_ps(simd[0],c),_mm_mul_ps(simd[1],s));
			simd[1] = _mm_sub_ps(_mm_mul_ps(simd[1],c),_mm_mul_ps(simd[0],s));
			simd[0] = tmp;
		}

		// other (wtf is 'other', matt?)
		f32 dot(const mstatic &o) const {
			_v128 t = _mm_mul_ps(simd[0],o.simd[0]);
			t = _mm_add_ps(t,_mm_mul_ps(simd[1],o.simd[1]));
			t = _mm_add_ps(t,_mm_mul_ps(simd[2],o.simd[2]));
			t = _mm_add_ps(t,_mm_mul_ps(simd[3],o.simd[3]));
			return ((float*)&t)[0]+((float*)&t)[1]+((float*)&t)[2]+((float*)&t)[3];
		}

		void transpose(mstatic &dest) const {
			for (u32_fast x=0, y; x<4; ++x) {
				dest.set(y,x,get(x,y));
			}
		}

		mstatic transpose() const {
			mstatic tmp;
			transpose(tmp);
			return tmp;
		}

		void transpose_inplace() {
			f32 tmp;
			for (u32_fast x=0, y; x<4; ++x) {
				tmp = get(x,y);
				set(x,y,get(y,x));
				set(y,x,tmp);
			}
		}
	};

	typedef mstatic<f32,4,4> m4;
	#undef depsimd
	#undef dep_data
	#undef dep_height
	#undef dep_elements
}

#endif
